/**
 * Copyright (C) 2025 pony working team
 * SPDX-License-Identifier: MulanPSL-2.0
 */

/* includes (local) ----------------------------------------------------------*/
#include <iaper.h>
/* includes (standard library, system) ---------------------------------------*/
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
/* includes (other library) --------------------------------------------------*/
/* includes (project) --------------------------------------------------------*/
#include <hal_delay.h>
#include <hal_sys.h>
#include <hal_part.h>

#include <k_task.h>

/* defines -------------------------------------------------------------------*/
/* typedefs ------------------------------------------------------------------*/
/* macro ---------------------------------------------------------------------*/
/* variables (extern) --------------------------------------------------------*/
/* variables (local) ---------------------------------------------------------*/
/* variables (global) --------------------------------------------------------*/
/* inlines -------------------------------------------------------------------*/
/* functions -----------------------------------------------------------------*/

static int iaper_abortboot(iaper_t *iap, uint8_t bootdelay)
{
    int abort = 0;
    unsigned char key;

    printf("Autostarting. Press shift + z key to abort.. %2d ", bootdelay);
    fflush(stdout);

    while ((bootdelay-- > 0) && (!abort)) {
        /* delay 100 * 10ms */
        for (int i = 0; !abort && i < 100; ++i) {
            /* we got key is 'Z' */
            key = iap->getc(iap->arg, 0);
            if (key == 'Z') {
                abort = 1;
                bootdelay = 0;
                break;
            }
            DELAY_MS(10);
        }
        printf("\b\b\b%2d ", bootdelay);
        fflush(stdout);
    }
    printf("\n");

    return abort;
}

static int iaper_download(iaper_t *iap)
{
    printf("Waiting for the file to be sent ... (press 'a' to abort)\n");

    int ret = iap->downloader(iap->arg);
    if (ret == 0) {
        DELAY_MS(100);
        printf("\nProgramming Completed Successfully!\n");
        if (iap->filesize != NULL || iap->filename != NULL) {
            printf("--------------------------------\n");
            if (iap->filename != NULL) {
                printf(" filename: %s\n", iap->filename(iap->arg));
            }
            if (iap->filesize != NULL) {
                printf(" filesize: %u Bytes\n", iap->filesize(iap->arg));
            }
            printf("--------------------------------\n");
        }
    } else {
        printf("\nProgramming Failed, error code: %d\n", ret);
    }
    return ret;
}

static int iaper_load_app(iaper_t *iap)
{
    size_t app_addr = 0;
    if (hal_part_get_start_addr(IAP_LPAR_ID_APP, &app_addr) != 0) {
        return -1;
    }
    return hal_sys_ioctl(HAL_SYS_CMD_LOAD, app_addr);
}

static int iaper_menu(iaper_t *iap)
{
    while (1) {
        printf("\n");
        printf("==============================================\n");
        printf("          FIRMWARE UPDATE MENU\n");
        printf("----------------------------------------------\n");
        printf("  0. Reset System\n");
        printf("  1. Download New Application\n");
        printf("  2. Load Application\n");
        printf("----------------------------------------------\n");
        printf("Select option [0-2]> ");
        fflush(stdout);

        unsigned char key = iap->getc(iap->arg, HAL_MAX_TIMEOUT);
        printf("%c\n", key);

        if (key == 0x30) {
            hal_sys_ioctl(HAL_SYS_CMD_REBOOT);
        } else if (key == 0x31) {
            iaper_download(iap);
        } else if (key == 0x32) {
            iaper_load_app(iap);
        }
    }

    return 0;
}

int iaper_loop(uintptr_t arg)
{
    iaper_t *iap = (iaper_t *)arg;

    // sleep for a while to allow main application to finish
    task_msleep(10);

    if (!iaper_abortboot(iap, 2)) {
        iaper_load_app(iap);
    }

    return iaper_menu(iap);
}
